package cases;

import java.util.concurrent.PriorityBlockingQueue;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: fzykd
 * Date: 2022-10-21
 * Time: 22:45
 */
//先创建一个类来描述任务
class MyTask implements Comparable<MyTask>{
    //有要执行的任务是什么
    public Runnable runnable;
    //什么时间执行
    public long time;

    //生成构造方法
    public MyTask(Runnable runnable, long alter) {
        this.runnable = runnable;
        //这里传来的是一个相对时间
        this.time = alter + System.currentTimeMillis();
    }

    public int compareTo(MyTask o){return (int) (this.time-o.time);}

}

//管理是表示 + 组织 还要有一个类来组织上面任务
class MyTimer {
    //将上面的任务放进一个线程安全的阻塞队列当中 是由数据结构的阻塞队列来组织任务
    PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
    //这是一个优先级队列 堆的形式 首先要明确比较的方式什么

    //组织之后 要有一个方法 来布置任务
    public void schedule(Runnable runnable,long time){
        MyTask myTask = new MyTask(runnable,time);
        //每次布置一个任务之后 放进阻塞队列当中
        queue.put(myTask);
        //并且每次插入成功 也要检查一下新插入的任务有没有时间最靠前的
        synchronized (locker){
            locker.notify();
        }
    }



    Object locker = new Object();
    //放入阻塞队列当中之后 要有一个线程不停的去检查时间  有没有时间到了的 任务去执行
    //这是写在构造方法当中的
    public MyTimer(){
        Thread t = new Thread(() -> {
            while (true){
                //不断的从阻塞队列当中取出任务来比较现在的时间 是否到了
                try {
                    MyTask myTask = queue.take();

                    //如果当时间没到时候 这里的线程不停的取出来放回去 出现了忙等现象
                    //处理方式是 wait和notify
                    long curTime = System.currentTimeMillis();
                    if (curTime < myTask.time){
                        //(不用额外的写方法 去获取时间 这个任务本身就带有时间 run方法也是)
                        //小于说明时间还没有到 时间没到在放进阻塞队列当中

                        queue.put(myTask);
                        //在检查时间没到放回队列之后 设置一个等待时间
                        synchronized (locker){
                            locker.wait(myTask.time-curTime);
                        }
                    }else {
                        myTask.runnable.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}


//实现一个计时器
public class Demo4 {
    public static void main(String[] args) {
        //首先的一个问题就是 指定堆的比较规则
        //剩下的就是线程安全问题 如果不加锁 设置等待时间 出现忙等现象
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        },3000);
    }

    /**
     * 总结
     * 管理一个资源 首先是先描述(比如说具体的一些属性 方法的等)
     * 描述完之后 在是使用响应的数据结构来组织
     * 计时器 一个两个类来具体实现
     * 1.先描述一个任务中具体的属性 有要执行的任务 有执行任务的时间  还要实现CompereTo接口 明确堆的比较类型
     * 2.描述完之后 在组织 通过描述的相应的属性 选择合适的数据结构来组织 还有具体的调用的schedule方法
     * 3.之后在考虑线程安全和效率问题
     */
}
